package com.bucket.cloud.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bucket.cloud.common.core.exception.OpenAlertException;
import com.bucket.cloud.common.core.security.OpenClientDetails;
import com.bucket.cloud.common.core.util.BeanConvertUtils;
import com.bucket.cloud.common.core.util.RandomValueUtils;
import com.bucket.cloud.common.core.util.StringUtils;
import com.bucket.cloud.system.api.constant.SystemConstants;
import com.bucket.cloud.system.api.model.*;
import com.bucket.cloud.system.dao.AuthApiMapper;
import com.bucket.cloud.system.dao.GatewayInterfaceMapper;
import com.bucket.cloud.system.dao.SystemAppMapper;
import com.bucket.cloud.system.service.SystemAppService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author: cyq
 * @date: 2018/11/12 16:26
 * @description:
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class SystemAppServiceImpl extends ServiceImpl<SystemAppMapper, SystemApp> implements SystemAppService {

    @Autowired
    private SystemAppMapper baseAppMapper;
    @Autowired
    private JdbcClientDetailsService jdbcClientDetailsService;
    @Resource
    private GatewayInterfaceMapper gatewayInterfaceMapper;
    @Resource
    private AuthApiMapper authApiMapper;
    /**
     * token有效期，默认12小时
     */
    public static final int ACCESS_TOKEN_VALIDITY_SECONDS = 60 * 60 * 12;
    /**
     * token有效期，默认7天
     */
    public static final int REFRESH_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 7;

    /**
     * 查询应用列表
     *
     * @param systemAppPage
     * @return
     */
    @Override
    public IPage<SystemApp> findListPage(SystemAppPage systemAppPage) {
        IPage pageObj = new Page(systemAppPage.getPageNumber(), systemAppPage.getPageSize());
        return this.baseMapper.queryByParams(pageObj, systemAppPage);
    }

    @Override
    @CachePut(value = "apps", key = "#app.appId")
    public SystemApp addApp(SystemApp app) {
        String apiKey = RandomValueUtils.randomAlphanumeric(24);
        String secretKey = RandomValueUtils.randomAlphanumeric(32);
        app.setApiKey(apiKey);
        app.setSecretKey(secretKey);
        app.setCreateTime(new Date());
        app.setUpdateTime(app.getCreateTime());
        if (app.getIsPersist() == null) {
            app.setIsPersist(0);
        }
        baseAppMapper.insert(app);
        return app;
    }

    @Override
    @CachePut(value = "apps", key = "'client:'+#app.appId")
    public OpenClientDetails addClient(SystemApp app) {
        Map info = BeanConvertUtils.objectToMap(app);
        // 功能授权
        OpenClientDetails client = new OpenClientDetails(app.getApiKey(), "", app.getSystemAppClient().getScopes(), app.getSystemAppClient().getGrantTypes(), "", app.getSystemAppClient().getRedirectUrls());
        client.setClientId(app.getApiKey());
        client.setClientSecret(app.getSecretKey());
        client.setAdditionalInformation(info);
        client.setAccessTokenValiditySeconds(app.getSystemAppClient().getAccessTokenValidity() == null ? ACCESS_TOKEN_VALIDITY_SECONDS : app.getSystemAppClient().getAccessTokenValidity());
        client.setRefreshTokenValiditySeconds(app.getSystemAppClient().getRefreshTokenValidity() == null ? REFRESH_TOKEN_VALIDITY_SECONDS : app.getSystemAppClient().getRefreshTokenValidity());
        jdbcClientDetailsService.addClientDetails(client);
        return client;
    }

    @Override
    @CacheEvict(value = {"apps"}, key = "#app.appId")
    public SystemApp updateApp(SystemApp app) {
        app.setUpdateTime(new Date());
        baseAppMapper.updateById(app);
        return app;
    }

    @Override
    @CacheEvict(value = "apps", key = "'client:'+#app.appId")
    public OpenClientDetails updateClient(SystemApp app) {
        // 功能授权
        OpenClientDetails client = new OpenClientDetails(app.getSecretKey(), "", app.getSystemAppClient().getScopes(), app.getSystemAppClient().getGrantTypes(), "", app.getSystemAppClient().getRedirectUrls());
        client.setAccessTokenValiditySeconds(app.getSystemAppClient().getAccessTokenValidity() == null ? ACCESS_TOKEN_VALIDITY_SECONDS : app.getSystemAppClient().getAccessTokenValidity());
        client.setRefreshTokenValiditySeconds(app.getSystemAppClient().getRefreshTokenValidity() == null ? REFRESH_TOKEN_VALIDITY_SECONDS : app.getSystemAppClient().getRefreshTokenValidity());
        Map info = BeanConvertUtils.objectToMap(app);
        client.setAdditionalInformation(info);
        client.setClientId(app.getApiKey());
        jdbcClientDetailsService.updateClientDetails(client);
        return client;
    }

    /**
     * 获取app详情
     *
     * @param appId
     * @return
     */
    @Override
    public SystemApp getAppInfo(String appId) {
        SystemApp systemApp = baseAppMapper.selectById(appId);
        ClientDetails clientDetails = jdbcClientDetailsService.loadClientByClientId(systemApp.getApiKey());
        SystemAppClient client = new SystemAppClient();
        client.setAccessTokenValidity(clientDetails.getAccessTokenValiditySeconds());
        client.setRefreshTokenValidity(clientDetails.getRefreshTokenValiditySeconds());
        client.setRedirectUrls(client.getRedirectUrls());
        client.setGrantTypes(StringUtils.join(clientDetails.getAuthorizedGrantTypes(), ","));
        client.setScopes(StringUtils.join(clientDetails.getScope(), ","));
        systemApp.setSystemAppClient(client);
        return systemApp;
    }

    /**
     * 获取app和应用信息
     *
     * @return
     */
    @Override
    @Cacheable(value = "apps", key = "'client:'+#clientId")
    public OpenClientDetails getAppClientInfo(String clientId) {
        BaseClientDetails baseClientDetails = null;
        try {
            baseClientDetails = (BaseClientDetails) jdbcClientDetailsService.loadClientByClientId(clientId);
        } catch (Exception e) {
            return null;
        }
        String appId = baseClientDetails.getAdditionalInformation().get("appId").toString();
        OpenClientDetails openClient = new OpenClientDetails();
        BeanUtils.copyProperties(baseClientDetails, openClient, getNullPropertyNames(baseClientDetails));

        // 接口授权
        List<GrantedAuthority> auths = new ArrayList<>();
        List<SystemInterface> interfaces = gatewayInterfaceMapper.queryApiByClientId(appId);
        for (SystemInterface i : interfaces) {
            auths.add(new SimpleGrantedAuthority(i.getId()));
        }
        openClient.setAuthorities(auths);
        return openClient;
    }

    public static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for (java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    /**
     * 重置秘钥
     *
     * @param appId
     * @return
     */
    @Override
    @Caching(evict = {
            @CacheEvict(value = {"apps"}, key = "#appId"),
            @CacheEvict(value = {"apps"}, key = "'client:'+#appId")
    })
    public String restSecret(String appId) {
        SystemApp appInfo = getAppInfo(appId);
        if (appInfo == null) {
            throw new OpenAlertException(appId + "应用不存在!");
        }
        if (appInfo.getIsPersist().equals(SystemConstants.ENABLED)) {
            throw new OpenAlertException(String.format("保留数据,不允许修改"));
        }
        // 生成新的密钥
        String secretKey = RandomValueUtils.randomAlphanumeric(32);
        appInfo.setSecretKey(secretKey);
        appInfo.setUpdateTime(new Date());
        baseAppMapper.updateById(appInfo);
        jdbcClientDetailsService.updateClientSecret(appInfo.getApiKey(), secretKey);
        return secretKey;
    }

    /**
     * 删除应用
     *
     * @return
     */
    @Caching(evict = {
            @CacheEvict(value = {"apps"}, key = "#appId"),
            @CacheEvict(value = {"apps"}, key = "'client:'+#appId")
    })
    @Override
    public void removeApp(String appId) {
        SystemApp appInfo = getAppInfo(appId);
        if (appInfo == null) {
            throw new OpenAlertException(appId + "应用不存在!");
        }
        if (appInfo.getIsPersist().equals(SystemConstants.ENABLED)) {
            throw new OpenAlertException(String.format("保留数据,不允许删除"));
        }
        baseAppMapper.deleteById(appInfo.getAppId());
        // 移除应用权限
        authApiMapper.delete(new QueryWrapper<SystemAuthApi>().eq("AUTH_ID", appId));
        jdbcClientDetailsService.removeClientDetails(appInfo.getApiKey());
    }

    @Override
    public void enable(String[] ids, int status) {
        List<SystemApp> appList = new ArrayList<>();
        for (String appId : ids) {
            SystemApp app = new SystemApp();
            app.setAppId(appId);
            app.setStatus(status);
            appList.add(app);
        }
        this.updateBatchById(appList);
    }

    public static void main(String[] args) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String apiKey = String.valueOf(RandomValueUtils.randomAlphanumeric(24));
        String secretKey = String.valueOf(RandomValueUtils.randomAlphanumeric(32));
        System.out.println("apiKey=" + apiKey);
        System.out.println("secretKey=" + secretKey);
        System.out.println("encodeSecretKey=" + encoder.encode(secretKey));

        System.out.println("encodeSecretKey=" + encoder.encode("123456"));
    }


}
